package com.redsoft.spirit.filter;
 
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.springframework.beans.factory.annotation.Autowired;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.redsoft.spirit.entity.JwtPatternUrl;
import com.redsoft.spirit.entity.JwtProperty;
import com.redsoft.spirit.entity.ResultStatusCode;
import com.redsoft.spirit.entity.StatelessToken;
import com.redsoft.spirit.util.jwt.JwtHelper;

import io.jsonwebtoken.Claims;
 
/**
 * shiro无状态的访问控制过滤器
 * @author 杨雷
 * @version 1.0
 * @date 2017年3月21日
 */
public class StatelessAccessControlFilter extends AccessControlFilter{
 
	/*
	 * 注入配置文件类
	 */
	@Autowired
	private JwtProperty jwtProperty;
	
	@Autowired
	private JwtPatternUrl jwtPatternUrl;
	
    /**
     * 先执行：isAccessAllowed 再执行onAccessDenied
     *
     * isAccessAllowed：表示是否允许访问；mappedValue就是[urls]配置中拦截器参数部分，
     * 如果允许访问返回true，否则false；
     *
     * 如果返回true的话，就直接返回交给下一个filter进行处理。
     * 如果返回false的话，回往下执行onAccessDenied
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
           throws Exception {
       System.out.println("StatelessAuthcFilter.isAccessAllowed()");
       return false;
    }
 
    /**
     * onAccessDenied：表示当访问拒绝时是否已经处理了；如果返回true表示需要继续处理；
     * 如果返回false表示该拦截器实例已经处理了，将直接返回即可。
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
       System.out.println("StatelessAuthcFilter.onAccessDenied()");
      
      
//       //1、客户端生成的消息摘要
//       String clientDigest = request.getParameter("digest");
//      
//       //2、客户端传入的用户身份
       String userId = request.getParameter("accountName");
//      
//       //3、客户端请求的参数列表
//       Map<String, String[]> params = new HashMap<String, String[]>(request.getParameterMap());
//       params.remove("digest");//为什么要移除呢？签名或者消息摘要算法的时候不能包含digest.
//      
//       //4、生成无状态Token
//       StatelessToken token = new StatelessToken(username,params,clientDigest);
////     UsernamePasswordToken token = new UsernamePasswordToken(username,clientDigest);
//       try {
//           //5、委托给Realm进行登录
//           getSubject(request, response).login(token);
//       } catch (Exception e) {
//           e.printStackTrace();
//           //6、登录失败
//           onLoginFail(response);
//           return false;//就直接返回给请求者.
//       }
       
       //进行jwt的验证，如果验证通过则继续下面的，如果不通过，则在jwt验证过程中返回错误信息
       if(doJwtFilter(request, response)) {
    	   if(userId == null) {
    		   userId = "";
    	   }
           StatelessToken token = new StatelessToken(userId, null,"admin");
    	   getSubject(request, response).login(token);
    	   
    	   return true;
       }  
       
       return false;
    }
   
    //登录失败时默认返回401 状态码
    private void onLoginFail(ServletResponse response) throws IOException {
       HttpServletResponse httpResponse = (HttpServletResponse) response;
       httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
       httpResponse.getWriter().write("login error");
    }
 
    /**
     * 
     * 在进入这个shiro的无状态过滤器 前，先进行jwt认证，只要jwt认证通过了就可以直接让shiro的认证通过.
     * <pre>
     * 	在进入这个shiro的无状态过滤器 前，先进行jwt认证，
     * 只要jwt认证通过了就可以直接让shiro的认证通过，代码写死必通过的就行，不通过的话直接返回数据，就不走下面的认证方法了。
     * </pre>
     * 
     * @author 杨雷
     * @param request
     * @param response
     */
    private boolean doJwtFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException{
	 	Map resultMap = new HashMap<>();
		 
        HttpServletRequest httpRequest = (HttpServletRequest)request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        String url = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length());

        //判断是否是排除的URL，比如登录，注册，验证码等URL，则直接通行
        if (isInclude(url)){
        	
        	//如果是属于排除的URL，比如登录，注册，验证码等URL，则直接通行
//            chain.doFilter(httpRequest, httpResponse);
            return true;
        } 
        
        String auth = httpRequest.getHeader("Authorization");
        boolean isRightUser = true;
        
		if ((auth != null) && (auth.length() > 7)) {
			String HeadStr = auth.substring(0, 6).toLowerCase();
			if (HeadStr.compareTo("bearer") == 0) {

				auth = auth.substring(7, auth.length());
				
				Claims claims = JwtHelper.parseJWT(auth, jwtProperty.getBase64Secret());
				
				//如果验证数据通过了，则判断身份是否跟传过来的一致，以防盗用token
				if (claims != null) {
					
//					//获取一下客户端传过来的关键的身份id
//					String userId = request.getParameter("userId");
//					String jwtUserId = (String) claims.get("userId");
//					
//					if(!userId.equals(jwtUserId)) {
//						isRightUser = false;
//					}
					
//					chain.doFilter(request, response);
					return true;
				}
			}
		} 
        
        //验证不通过 
        httpResponse.setCharacterEncoding("UTF-8");    
        httpResponse.setContentType("application/json; charset=utf-8");   
        httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);  
  
        //将验证不通过的错误返回
        ObjectMapper mapper = new ObjectMapper();
        
        //如果身份是冒用的
        if(!isRightUser) {
            resultMap.put("status", ResultStatusCode.INVALID_USER_TOKEN.getErrorCode());
            resultMap.put("msg", ResultStatusCode.INVALID_USER_TOKEN.getErrorMsg());
        } else {
            resultMap.put("status", ResultStatusCode.INVALID_TOKEN.getErrorCode());
            resultMap.put("msg", ResultStatusCode.INVALID_TOKEN.getErrorMsg());
        }

        resultMap.put("data", null);
          
//        resultMsg = new ResultMsg<Object>(true, ResultStatusCode.INVALID_TOKEN.getErrorCode(), ResultStatusCode.INVALID_TOKEN.getErrorMsg(), null);  
        httpResponse.getWriter().write(mapper.writeValueAsString(resultMap)); 
        
        return false;  
    }
    
    /**
     * 是否需要过滤
     * @param url
     * @return
     */
    private boolean isInclude(String url) {
    	
        for (String patternUrl : jwtPatternUrl.getUrlPatterns()) {
        	Pattern p = Pattern.compile(patternUrl);
        	 Matcher m = p.matcher(url);
            if (m.find()) {
                return true;
            }
        }
        return false;
    }
}